﻿#include "dialogsoundcard.h"
#include <QAudioDevice>
#include <QDebug>
#include <QFileDialog>
#include <QLayout>
#include <QMediaDevices>
#include <QMessageBox>
#include <QTimer>
#include <QUrl>
#include "tb_interface.h"
#include "ui_dialogsoundcard.h"
std::atomic<long long> tx_watermark(0);
int ins_mark = 0;
DialogSoundCard::DialogSoundCard(QWidget *parent)
	: QDialog(parent)
	, ui(new Ui::DialogSoundCard)
	, m_devOutputModule(new QStandardItemModel(this))
	, m_beep(new QSoundEffect(this))
{
	ui->setupUi(this);
	ui->comboBox_audioDevices->setModel(m_devOutputModule);
	m_devOutputlist = QMediaDevices::audioOutputs();
	foreach (QAudioDevice info, m_devOutputlist)
		m_devOutputModule->appendRow(new QStandardItem(info.description()));

	//---------- players ----------
	miMaxValue = 0;
	miVolume = ui->horizontalSlider->value();

	mpAudioOutputSound = nullptr;
	mpOutDevSound = nullptr;

	ui->btn_stop->setDisabled(true);

	connect(ui->btn_start, SIGNAL(clicked()), this, SLOT(OnPlayStart()));
	connect(ui->btn_stop, SIGNAL(clicked()), this, SLOT(OnPlayStop()));

	m_beep->setVolume(1.0);
	m_beep->setLoopCount(1);
	m_beep->setSource(QUrl("qrc:/wav/ding.wav"));

	m_nTimerID = startTimer(10);
}

DialogSoundCard::DialogSoundCard(const TASKBUS::cmdlineParser *pline, QWidget *parent)
	: DialogSoundCard(parent)
{
	m_cmdline = pline;
	if (m_cmdline)
	{
		int sample_rate = m_cmdline->toInt("sample_rate", 48000);
		ins_mark = m_cmdline->toInt("tx_mark", 0);
		setInstance(m_cmdline->toInt("instance", 0));
		QString device = QString::fromStdString(m_cmdline->toString("device", "default"));
		int channel = m_cmdline->toInt("channel", 2);

		ui->spinbox_channels->setValue(channel);
		ui->spinbox_sprate->setValue(sample_rate);
		QList<QStandardItem *> items = m_devOutputModule->findItems(device);
		if (items.size())
		{
			QModelIndex nitem = m_devOutputModule->indexFromItem(items.first());
			if (nitem.row() >= 0 && nitem.row() < m_devOutputModule->rowCount())
				ui->comboBox_audioDevices->setCurrentIndex(nitem.row());
		}

		//Listen thread to recieve messages from platform
		m_pListenThread = new listen_thread(m_cmdline, this);
		connect(m_pListenThread, &listen_thread::quit_app, this, &DialogSoundCard::close);
		connect(m_pListenThread, &listen_thread::sig_play, this, &DialogSoundCard::sltPlay);
		connect(m_pListenThread, &listen_thread::sig_beep, this, &DialogSoundCard::sltBeep);
		m_pListenThread->start();

		int hiden = m_cmdline->toInt("hide", 0);
		int autostart = m_cmdline->toInt("autostart", 0);

		if (hiden || autostart)
			OnPlayStart();
	}
}

DialogSoundCard::~DialogSoundCard()
{
	if (m_pListenThread)
		m_pListenThread->terminate();
	delete ui;
}

void DialogSoundCard::OnPlayStart()
{
	m_bFirstPlay = true;
	ui->btn_start->setDisabled(true);
	ui->btn_stop->setDisabled(false);
	InitMonitor();
}

void DialogSoundCard::OnPlayStop()
{
	ui->btn_start->setDisabled(false);
	ui->btn_stop->setDisabled(true);
	if (mpOutDevSound != 0)
	{
		disconnect(mpOutDevSound, 0, this, 0);
		mpOutDevSound = 0;
	}
}

void DialogSoundCard::OnStateChange(QAudio::State state)
{
	if (state == QAudio::IdleState)
		OnPlayStop();
}

void DialogSoundCard::InitMonitor()
{
	mFormatSound.setSampleFormat(QAudioFormat::Int16); //set sample sze to 16 bit
	mFormatSound.setSampleRate(ui->spinbox_sprate->value());
	mFormatSound.setChannelCount(ui->spinbox_channels->value());

	//QAudioDevice infoOut(QMediaDevices::defaultAudioOutput());
	QAudioDevice infoOut;
	if (ui->comboBox_audioDevices->currentIndex() >= 0
		&& ui->comboBox_audioDevices->currentIndex() < m_devOutputModule->rowCount())
		infoOut = m_devOutputlist[ui->comboBox_audioDevices->currentIndex()];
	else
		infoOut = QMediaDevices::defaultAudioOutput();

	if (!infoOut.isFormatSupported(mFormatSound))
	{
		//Default format not supported - trying to use nearest
		//mFormatSound = infoOut.preferredFormat();
	}

	CreateAudioOutput();
	mpOutDevSound = mpAudioOutputSound->start();
	connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int)));
}

void DialogSoundCard::CreateAudioOutput()
{
	if (mpOutDevSound != 0)
	{
		disconnect(mpOutDevSound, 0, this, 0);
		mpOutDevSound = 0;
	}

	QAudioDevice outputDevice(QMediaDevices::defaultAudioOutput());
	mpAudioOutputSound = new QAudioSink(outputDevice, mFormatSound, this);
	const int bBuffer20ms = 0.04 * ui->spinbox_sprate->value() * 2 * ui->spinbox_channels->value();
	mpAudioOutputSound->setBufferSize(bBuffer20ms * 8);
}
void DialogSoundCard::sltPlay(QByteArray v)
{
	miMaxValue = 0;
	if (mpOutDevSound)
	{
		qint16 *outdata = (qint16 *) v.data();
		int len = v.size() / 2;
		miMaxValue = 0;
		for (int iIndex = 0; iIndex < len; iIndex++)
		{
			//Change volume to each integer data in a sample
			int value = ApplyVolumeToSample(outdata[iIndex]);
			outdata[iIndex] = value;

			miMaxValue = miMaxValue >= value ? miMaxValue : value;
		}
		//mpOutDevSound->write(v);
		m_buffer.append(v);
		tx_watermark = m_buffer.size();
	}
	ui->progress->setValue(miMaxValue);
}

void DialogSoundCard::sltBeep(QString /*name*/)
{
	if (!m_beep->isPlaying())
		m_beep->play();
}

int DialogSoundCard::ApplyVolumeToSample(short iSample)
{
	//Calculate volume, Volume limited to  max 30000 and min -30000
	return std::max(std::min(((iSample * miVolume) / 50), 30000), -30000);
}

void DialogSoundCard::OnSliderValueChanged(int value)
{
	miVolume = value;
}

void DialogSoundCard::timerEvent(QTimerEvent *e)
{
	if (e->timerId() == m_nTimerID && mpAudioOutputSound)
	{
		const int bBuffer20ms = 0.04 * ui->spinbox_sprate->value() * 2
								* ui->spinbox_channels->value();
		if (mpAudioOutputSound->bytesFree() && m_buffer.size() >= bBuffer20ms)
		{
			int w = mpOutDevSound->write(m_buffer.data(), bBuffer20ms);
			if (w)
			{
				m_buffer.remove(0, w);
				tx_watermark = m_buffer.size();
			}
		}
		if (ins_mark)
		{
			long long pm = tx_watermark;
			TASKBUS::push_subject((unsigned int) ins_mark,
								  (unsigned int) m_n_instance,
								  sizeof(pm),
								  (const unsigned char *) &pm,
								  nullptr);
		}
	}
}
